/*
How to play?

wasd to change direction

*/



#include <stdio.h>
#include <string.h>
#include<stdlib.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <termios.h>
#include <unistd.h>
#define MapSizei 25
#define MapSizej 50
static struct termios ori_attr, cur_attr;

static __inline
int tty_reset(void)
{
        if (tcsetattr(STDIN_FILENO, TCSANOW, &ori_attr) != 0)
                return -1;

        return 0;
}


static __inline
int tty_set(void)
{

        if ( tcgetattr(STDIN_FILENO, &ori_attr) )
                return -1;

        memcpy(&cur_attr, &ori_attr, sizeof(cur_attr) );
        cur_attr.c_lflag &= ~ICANON;
//        cur_attr.c_lflag |= ECHO;
        cur_attr.c_lflag &= ~ECHO;
        cur_attr.c_cc[VMIN] = 1;
        cur_attr.c_cc[VTIME] = 0;

        if (tcsetattr(STDIN_FILENO, TCSANOW, &cur_attr) != 0)
                return -1;

        return 0;
}

static __inline
int kbhit(void)
{

        fd_set rfds;
        struct timeval tv;
        int retval;

        /* Watch stdin (fd 0) to see when it has input. */
        FD_ZERO(&rfds);
        FD_SET(0, &rfds);
        /* Wait up to five seconds. */
        tv.tv_sec  = 0;
        tv.tv_usec = 0;

        retval = select(1, &rfds, NULL, NULL, &tv);
        /* Don't rely on the value of tv now! */

        if (retval == -1) {
                perror("select()");
                return 0;
        } else if (retval)
                return 1;
        /* FD_ISSET(0, &rfds) will be true. */
        else
                return 0;
        return 0;
}

//将你的 snake 代码放在这里
typedef struct body_node{//蛇身节点
    int i;
    int j;
    int isHead;
    struct body_node* prev;
}Body_node;

typedef struct snake{//蛇类型
    Body_node* head;
    Body_node* tail;
    int tail_i;//辅助变量
    int tail_j;//方便尾部增长操作
    int l;
}Snake;

typedef struct map{//地图类型
    int have_food;
    int foodi;
    int foodj;
    int sizei;
    int sizej;
}Map;

Body_node* insert_body_node(Body_node* tail,int i,int j);
int have_such_node(Snake* snake,int i,int j);
/*以上函数单纯为一些操作链表的函数*/
Map* get_map(Snake** snake);
Snake* initialize_snake(int i,int j);//i,j分别表示蛇头初始位置，默认从左到右横向摆放
/*上述两个为构造函数，用来初始化对象蛇和对象地图*/
void add_tail_after_eat(Snake* snake);//尾巴增长的函数
void put_an_apple(Snake** snake,Map* map);//随机放置苹果的函数
void move(Snake* snake,char direct);//移动函数
int check_if_crash(Snake** snake,int sizei,int sizej);//检查碰撞的函数
int check_if_eaten(Snake* snake,Map* map);//检查食物是否被吃掉的函数
char select_direction(Snake** snake,Map* map);//简单智能
void print(Snake** snake,Map* map);//打印游戏画面的函数
void delete_snake(Snake* snake);//蛇的析构函数

int main()
{
        //设置终端进入非缓冲状态
        int tty_set_flag;
        tty_set_flag = tty_set();
        //将你的 snake 代码放在这里
        Snake** snakes=(Snake**)malloc(sizeof(Snake*)*2);
        snakes[0]=initialize_snake(MapSizei/2+6,MapSizej/2);
        snakes[1]=initialize_snake(MapSizei/2,MapSizej/2);
        Map* map=get_map(snakes);
        char order='d';
        char trash;
        int player_goal=0;
        int ai_goal=0;
        /*
        循环的逻辑
        1.非阻塞接收用户输入
        2.两条蛇移动
        3.打印游戏画面
        4.做出各种判定（食物被吃，蛇身碰撞）
        */
        while(1) {
            printf("\033c");
            if( kbhit() ) {
                char new_order = getchar();
                if((new_order=='w'&&order=='s')||(new_order=='s'&&order=='w')||(new_order=='a'&&order=='d')||(new_order=='d'&&order=='a')){//防止头反向吃身子
                    ;
                }else{
                    order=new_order;
                }
            } else {
                ;// fprintf(stderr, "<no key detected>\n");
            }
            move(snakes[0],order);
            move(snakes[1],select_direction(snakes,map));
            print(snakes,map);
            printf("\033[;33mplayer goal:%d\033[0m\nai goal:%d\n",player_goal,ai_goal);
            if(check_if_crash(snakes,map->sizei,map->sizej)){//如果碰撞
                printf("Game over!\n");
                break;
            }
            if(check_if_eaten(snakes[0],map)){//如果苹果被player吃了
                put_an_apple(snakes,map);
                add_tail_after_eat(snakes[0]);
                player_goal++;
            }
            if(check_if_eaten(snakes[1],map)){//如果苹果被ai吃了
                put_an_apple(snakes,map);
                add_tail_after_eat(snakes[1]);
                ai_goal++;
            }

            usleep(150000);
        }
        if(player_goal>=ai_goal){
            printf("Player win!\n");
        }else{
            printf("AI win!\n");
        }
        delete_snake(snakes[0]);
        delete_snake(snakes[1]);
        free(snakes);
        free(map);

        //恢复终端设置
        if(tty_set_flag == 0)
                tty_reset();
        return 0;
}

/*implementation*/
Body_node* insert_body_node(Body_node* tail,int i,int j){
    Body_node* add=(Body_node*)malloc(sizeof(Body_node));
    add->i=i;
    add->j=j;
    add->isHead=0;
    add->prev=tail;
    return add;
}

int have_such_node(Snake* snake,int i,int j){
    Body_node* search=snake->tail;
    while(search!=NULL){
        if(search->i==i&&search->j==j&&search->isHead==0)return 1;
        if(search->i==i&&search->j==j&&search->isHead==1)return 2;
        search=search->prev;
    }
    return 0;
}

Map* get_map(Snake** snakes){
    Map* map=(Map*)malloc(sizeof(Map));
    map->sizei=MapSizei;
    map->sizej=MapSizej;
    map->have_food=0;
    put_an_apple(snakes,map);
    return map;
}

Snake* initialize_snake(int i,int j){
    Snake* snake=(Snake*)malloc(sizeof(Snake));
    snake->head=(Body_node*)malloc(sizeof(Body_node));
    snake->head->i=i;
    snake->head->j=j;
    snake->head->isHead=1;
    snake->head->prev=NULL;
    snake->tail=snake->head;
    snake->tail=insert_body_node(snake->tail,i,j-1);
    snake->tail=insert_body_node(snake->tail,i,j-2);
    snake->tail_i=i;
    snake->tail_j=j;
    snake->l=3;//默认长度为3
    return snake;
}

void add_tail_after_eat(Snake* snake){
    snake->tail=insert_body_node(snake->tail,snake->tail_i,snake->tail_j);
    snake->l+=1;
}


void put_an_apple(Snake** snakes,Map* map){
    int rand_i=rand()%(map->sizei-2)+1;
    int rand_j=rand()%(map->sizej-2)+1;
    while(have_such_node(snakes[0],rand_i,rand_j)!=0&&have_such_node(snakes[1],rand_i,rand_j)!=0){
        rand_i=rand()%(map->sizei-2)+1;
        rand_j=rand()%(map->sizej-2)+1;
    }
    map->foodi=rand_i;
    map->foodj=rand_j;
    map->have_food=1;
}



void move(Snake* snake,char direct){//移动函数
    snake->tail_i=snake->tail->i;
    snake->tail_j=snake->tail->j;
    Body_node* search=snake->tail;
    while(search->prev!=NULL){//将蛇身体整体向前一步
        search->i=search->prev->i;
        search->j=search->prev->j;
        search=search->prev;
    }
    switch(direct){//再将蛇头前进一步
        case 'w':
            snake->head->i-=1;
            break;
        case 'a':
            snake->head->j-=1;
            break;
        case 's':
            snake->head->i+=1;
            break;
        case 'd':
            snake->head->j+=1;
            break;
        default:
            break;

    }
}

int check_if_crash(Snake** snakes,int sizei,int sizej){
    Body_node* player=snakes[0]->tail;
    Body_node* ai=snakes[1]->tail;
    while(player!=NULL){
        if(player->i<=0||player->i>=sizei-1||player->j<=0||player->j>=sizej-1)return 1;//玩家出界判断
        if(have_such_node(snakes[1],player->i,player->j))return 1;//玩家是否碰撞ai
        Body_node* search=snakes[0]->tail;
        while(search!=NULL){
            if(player!=search&&player->i==search->i&&player->j==search->j)return 1;//玩家碰撞自身判断
            search=search->prev;
        }
        player=player->prev;
    }
    while(ai!=NULL){
        if(ai->i<=0||ai->i>=sizei-1||ai->j<=0||ai->j>=sizej-1)return 1;//ai出界判断
        ai=ai->prev;
    }
    return 0;
}

int check_if_eaten(Snake* snake,Map* map){
    if(have_such_node(snake,map->foodi,map->foodj)){//玩家吃了
        map->have_food=0;
        return 1;
    }
    return 0;
}

int isMoveValid(Snake** snakes,int headi,int headj,Map* map){
    if(headi<=0||headi>=map->sizei-1||headj<=0||headj>=map->sizej-1)return 0;
    if(have_such_node(snakes[0],headi,headj))return 0;//ai不能碰玩家
    if(have_such_node(snakes[1],headi,headj))return 0;//ai不能碰自己
    return 1;
}

char select_direction(Snake** snakes,Map* map){
    char direct[4]={'w','a','s','d'};
    int dis[4]={99999,99999,99999,99999};
    int valid[4]={0,0,0,0};
    int di[4]={-1,0,1,0};
    int dj[4]={0,-1,0,1};
    for(int i=0;i<=3;i++){
        dis[i]=abs(snakes[1]->head->i+di[i]-map->foodi)+abs(snakes[1]->head->j+dj[i]-map->foodj);//计算曼哈顿距离
        valid[i]=isMoveValid(snakes,snakes[1]->head->i+di[i],snakes[1]->head->j+dj[i],map);//判断这个方向是否有效
    }
    for(int i=0;i<=2;i++){
        for(int j=i+1;j<=3;j++){
            if(dis[j]<dis[i]){
                int temp=dis[i];
                dis[i]=dis[j];
                dis[j]=temp;
                temp=valid[i];
                valid[i]=valid[j];
                valid[j]=temp;
                char t=direct[i];
                direct[i]=direct[j];
                direct[j]=t;
            }
        }
        if(valid[i]==1)return direct[i];
    }
    return direct[3];
}




void print(Snake** snakes,Map* map){
    for(int i=0;i<=map->sizei-1;i++){
        for(int j=0;j<=map->sizej-1;j++){
            if(i==0||i==map->sizei-1||j==0||j==map->sizej-1){
                printf("W");
            }else{
                int choose=have_such_node(snakes[0],i,j);
                if(choose){
                    if(choose==1)printf("\033[;33mX\033[0m");//player is yellow
                    if(choose==2)printf("\033[;33mH\033[0m");
                }else{
                    choose=have_such_node(snakes[1],i,j);
                    if(choose){
                        if(choose==1)printf("X");
                        if(choose==2)printf("H");
                    }else{
                        if(map->foodi==i&&map->foodj==j&&map->have_food==1){
                            printf("A");
                        }else{
                            printf(" ");
                        }
                    }
                }


            }

        }
        printf("\n");
    }
}

void delete_snake(Snake* snake){//蛇的析构函数
    Body_node* search=snake->tail;
    while(search!=NULL){
        Body_node* temp=search;
        search=search->prev;
        free(temp);
    }
}
